package com.yhn.shortlink.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yhn.shortlink.admin.dto.req.user.UserLoginReqDTO;
import com.yhn.shortlink.admin.dto.req.user.UserRegisterReqDTO;
import com.yhn.shortlink.admin.dto.req.user.UserUpdateReqDTO;
import com.yhn.shortlink.admin.dto.resp.user.UserLoginRespDTO;
import com.yhn.shortlink.admin.dto.resp.user.UserRespDTO;
import com.yhn.shortlink.admin.service.UserService;
import com.yhn.shortlink.common.constant.RedisConstant;
import com.yhn.shortlink.common.entity.UserDO;
import com.yhn.shortlink.common.enums.UserErrorEnums;
import com.yhn.shortlink.common.exception.BusinessException;
import com.yhn.shortlink.common.exception.ThrowUtils;
import com.yhn.shortlink.admin.mapper.UserMapper;
import com.yhn.shortlink.common.model.ErrorCode;
import jakarta.annotation.Resource;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @author 86177
 * @description 针对表【user】的数据库操作Service实现
 * @createDate 2024-07-21 13:25:47
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO>
        implements UserService {

    @Resource
    private RBloomFilter<String> userRegisterCachePenetrationBloomFilter;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public UserRespDTO getUserByUserName(String userName) {
        LambdaQueryWrapper<UserDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserDO::getUsername, userName);
        UserDO userDO = baseMapper.selectOne(wrapper);
        ThrowUtils.throwIf(userDO == null, ErrorCode.USER_NULL);
        UserRespDTO userRespDto = new UserRespDTO();
        BeanUtils.copyProperties(userDO, userRespDto);
        return userRespDto;
    }

    @Override
    public Boolean availableUsername(String userName) {
        //如果布隆过滤器存在username返回false，表示用户不存在
        return !userRegisterCachePenetrationBloomFilter.contains(userName);
    }

    /**
     * 注册用户。
     *
     * @param userRegisterReqDTO 用户注册请求数据传输对象，包含了待注册用户的详细信息，如用户名、密码等。
     * @throws RuntimeException 如果用户名已经存在，则抛出异常，阻止注册流程继续。
     */
    @Override
    public Boolean register(UserRegisterReqDTO userRegisterReqDTO) {
        //如果存在用户名则异常
        ThrowUtils.throwIf(!availableUsername(userRegisterReqDTO.getUsername()), UserErrorEnums.USER_NAME_EXITS);
        //添加分布式锁
        RLock lock = redissonClient.getLock(RedisConstant.LOCK_USER_REGISTER_KEY + userRegisterReqDTO.getUsername());
        //tryLock成功则添加到数据库和布隆过滤器
        try {
            if (lock.tryLock()) {
                // 将注册请求数据转换为内部数据格式，并插入到数据库中
                int insert = baseMapper.insert(BeanUtil.toBean(userRegisterReqDTO, UserDO.class));
                ThrowUtils.throwIf(insert == 0, UserErrorEnums.USER_SAVE_ERROR);
                //向布隆过滤器添加数据
                userRegisterCachePenetrationBloomFilter.add(userRegisterReqDTO.getUsername());
                return true;
            } else {
                throw new BusinessException(UserErrorEnums.USER_NAME_EXITS);
            }
        } finally {
            lock.unlock();
        }

    }

    @Override
    public Boolean updateInfo(UserUpdateReqDTO userUpdateReqDTO) {
        //TODO 验证是否为当前登录的用户

        //要根据用户名查询
        LambdaQueryWrapper<UserDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserDO::getUsername, userUpdateReqDTO.getUsername());
        int update = baseMapper.update(BeanUtil.toBean(userUpdateReqDTO, UserDO.class), wrapper);
        ThrowUtils.throwIf(update == 0, UserErrorEnums.USER_UPDATE_ERROR);
        return true;
    }

    @Override
    public UserLoginRespDTO login(UserLoginReqDTO userLoginReqDTO) {
        LambdaQueryWrapper<UserDO> queryWrapper = Wrappers.lambdaQuery(UserDO.class)
                .eq(UserDO::getUsername, userLoginReqDTO.getUsername())
                .eq(UserDO::getPassword, userLoginReqDTO.getPassword())
                .eq(UserDO::getDelFlag, 0);
        UserDO userDO = baseMapper.selectOne(queryWrapper);
        ThrowUtils.throwIf(userDO == null, UserErrorEnums.USER_NULL);
        Boolean hasLogin = stringRedisTemplate.hasKey(RedisConstant.USER_LOGIN_KEY_PREFIX + userLoginReqDTO.getUsername());
        if (hasLogin != null && hasLogin) {
            throw new BusinessException(UserErrorEnums.USER_LOGIN_ERROR);
        }
        //不能让用户重复登录
        //利用Hash结构存储 key:login_username  Value: key:token Val:用户信息
        UUID uuid = UUID.randomUUID();
        //将token存入redis

        stringRedisTemplate.opsForHash().put(RedisConstant.USER_LOGIN_KEY_PREFIX + userLoginReqDTO.getUsername(), uuid.toString(), JSONUtil.toJsonStr(userDO));
        stringRedisTemplate.expire(RedisConstant.USER_LOGIN_KEY_PREFIX + userLoginReqDTO.getUsername(), RedisConstant.REDIS_EXPIRE, TimeUnit.DAYS);
        return new UserLoginRespDTO().setToken(uuid.toString());
    }

    @Override
    public Boolean checkLogin(String username, String token) {

        return stringRedisTemplate.opsForHash().get(RedisConstant.USER_LOGIN_KEY_PREFIX + username, token) != null;
    }

    @Override
    public Boolean logout(String username, String token) {
        if (checkLogin(username, token)) {
            stringRedisTemplate.delete(RedisConstant.USER_LOGIN_KEY_PREFIX + username);
            return true;
        }
        throw new BusinessException(UserErrorEnums.USER_NOT_LOGIN);
    }
}




